#include <Comm/Include/Channel.h>

Channel::Channel(SyncQueue* _Tx_Queue, SyncQueue* _Rx_Queue, TCPSocket* _ipc_resource, Semaphore* _sem_sync_Channel_tx_rx_tasks)
{
    this->sem_sync_canal_tx_rx_tasks = _sem_sync_Channel_tx_rx_tasks;
    this->ipc_resource = _ipc_resource;
    this->RxQueue = _Rx_Queue;
    this->TxQueue = _Tx_Queue;
    this->sending_closed = false;
    this->receiving_closed = false;
}

Channel::~Channel(void)
{
    terminate();

    delete this->RxQueue;
    delete this->TxQueue;
    
    this->sem_sync_canal_tx_rx_tasks->wait();
    this->sem_sync_canal_tx_rx_tasks->wait();
    
    delete this->ipc_resource;
    delete this->sem_sync_canal_tx_rx_tasks;
}

void Channel::send(SimpleMessage* msg)
{
    try
    {
        TxQueue->push(msg);
    }
    catch (SyncQueue::SyncQueueExceptionType queue_already_closed_exception)
    {
        delete msg;
        throw ChannelExceptionType::SENDING_CLOSED;
    }
}

SimpleMessage* Channel::receive(unsigned int timeout)
{
    SimpleMessage* msg = NULL;
    try
    {
        msg = RxQueue->pop(timeout);
    }
    catch (SyncQueue::SyncQueueExceptionType queue_exception)
    {
        switch (queue_exception)
        {
        case SyncQueue::SyncQueueExceptionType::QUEUE_ALREADY_CLOSED:
            throw ChannelExceptionType::RECEPTION_CLOSED;
        	break;
        case SyncQueue::SyncQueueExceptionType::POP_TIMEOUT:
            throw ChannelExceptionType::RECEPTION_TIMEOUT;
            break;
        }
    }
    return msg;
}

void Channel::installAsyncReceiveFunction(TCallbackAsyncReceiveFunction f, void* user_data)
{
    this->RxQueue->installAsyncReceiveFunction(f, user_data);
}

void* Channel::uninstallAsyncReceiveFunction()
{
    return this->RxQueue->uninstallAsyncReceiveFunction();
}

SyncQueue* Channel::getRxQueue()
{
    return this->RxQueue;
}
SyncQueue* Channel::getTxQueue()
{
    return this->TxQueue;
}

void Channel::terminateSending()
{
    this->sending_closed =  true;
    this->TxQueue->closeAndWaitTillEmpty();
    this->ipc_resource->terminateWriting();
}

void Channel::terminateReceiving()
{
    this->receiving_closed = true;
    this->ipc_resource->terminateReading();
    this->RxQueue->closeAndDiscardAll();
}

void Channel::terminate()
{
    this->sending_closed =  true;
    this->receiving_closed = true;
    this->TxQueue->closeAndWaitTillEmpty();
    this->RxQueue->closeAndDiscardAll();
    this->ipc_resource->terminate();
}
